package ip

import (
	"context"
	"fmt"
	"strings"
	"time"

	"xtsec.com/dns-syncer/common/ip/pool"
	"xtsec.com/dns-syncer/common/tools"
	"xtsec.com/dns-syncer/common/tools/async"
)

type ipClient interface {
	GetIPResponse(context.Context) (pool.IPResponse, error)
}

func getIPClients() []ipClient {
	return []ipClient{
		// pool.NewIpshu(), // 不准确
		pool.NewPconline(),
		pool.NewIpify(),
		pool.NewToolLu(),
		pool.NewHttpBin(),
	}
}

func Get() (string, error) {
	clients := getIPClients()
	group := async.NewGroupFirst[*pool.IPResponse]()
	notSleepIndex := tools.RandIndex(clients)

	for i, client := range clients {
		func(i int, _client ipClient) {
			group.Add(func(ctx context.Context) (async.GroupFirstResult[*pool.IPResponse], error) {
				if i != notSleepIndex {
					seconds := tools.RandInt(0, 5)
					time.Sleep(time.Duration(seconds) * time.Second)
				}

				resp, err := _client.GetIPResponse(ctx)
				if err != nil {
					return nil, err
				}
				return resp, nil
			})
		}(i, client)
	}

	result, err := group.Wait()
	if err != nil {
		return "", err
	}

	resp := result.Value()
	fmt.Printf("get public ip successful. target: %s, ip: %s\n", resp.Target, resp.Ip)
	return resp.Ip, nil
}

func GetAll() (map[string]string, error) {
	clients := getIPClients()
	group := async.NewGroup[*pool.IPResponse](len(clients))
	ctx := context.Background()

	for i, client := range clients {
		func(i int, _client ipClient) {
			group.Add(func() (*pool.IPResponse, error) {
				resp, err := _client.GetIPResponse(ctx)
				if err != nil {
					return nil, err
				}
				return &resp, nil
			}, "")
		}(i, client)
	}

	result, err := group.Collect()
	if err != nil {
		return nil, err
	}

	ipMap := make(map[string]string, len(result))
	for res := range result {
		ipMap[res.Data.Target] = res.Data.Ip
	}

	// print all target:ip table
	showGetAllInfo(ipMap)

	return ipMap, nil
}

func showGetAllInfo(ipMap map[string]string) {
	items := make([]string, 0, len(ipMap))
	for target, ip := range ipMap {
		items = append(items, fmt.Sprintf("\ttarget: %s, ip: %s", target, ip))
	}
	showStr := fmt.Sprintf("get public ips successful.\ninfo:\n%s\n", strings.Join(items, "\n"))

	fmt.Println(showStr)
}
